﻿/*
	Copyright 2010 MCSharp team (Modified for use with MCZall/MCLawl) Licensed under the
	Educational Community License, Version 2.0 (the "License"); you may
	not use this file except in compliance with the License. You may
	obtain a copy of the License at
	
	http://www.osedu.org/licenses/ECL-2.0
	
	Unless required by applicable law or agreed to in writing,
	software distributed under the License is distributed on an "AS IS"
	BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
	or implied. See the License for the specific language governing
	permissions and limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Threading;

namespace ForgeCraft
{
    public class Physics
    {
        public int x;
        public byte y;
        public int z;

        public Thread physThread;
        public bool physPause = false;
        public DateTime physResume;
        public System.Timers.Timer physTimer = new System.Timers.Timer(1000);
        public int physics = 0;
        
        public int speedPhysics = 250;
        public int overload = 1500;

        
        public byte[] blocks;
        public struct Zone { public ushort smallX, smallY, smallZ, bigX, bigY, bigZ; public string Owner; }
        
        public int lastCheck = 0;
        public int lastUpdate = 0;

        List<Check> ListCheck = new List<Check>();  //A list of blocks that need to be updated
        List<Update> ListUpdate = new List<Update>();  //A list of block to change after calculation

        public class Check
        {
            public Position ps;
            public int b;
            public byte time;
            public string extraInfo = "";
            public Check(Position ps, int b, string extraInfo = "")
            {
                this.ps = ps;
                this.b = b;
                time = 0;
                this.extraInfo = extraInfo;
            }
        }
        //-------------------------------------------------------------------------------------------------------------------------------------------------------
        public class Update
        {
            public Position ps;
            public int b;
            public byte type;
            public string extraInfo = "";
            public Update(Position ps, int b, byte type, string extraInfo = "")
            {
                this.ps = ps;
                this.b = b;
                this.type = type;
                this.extraInfo = extraInfo;
            }
        }

        public void Blockchange(Player p, int x, byte y, int z, byte type) { Blockchange(p, x, y, z, type, true); }

        public void Blockchange(Player p, int x, byte y, int z, byte type, bool addaction)
        {
            //TODO: Get this working with SMP!

            string errorLocation = "start";
        retry: try
            {
                ChunkBlock b = Server.world.GetBlock(x, y, z);

                /*errorLocation = "Block rank checking";
                if (!Block.AllowBreak(b))
                {
                    if (!Block.canPlace(p, b))
                    {
                        return;
                    }
                }*/

                /*errorLocation = "Map rank checking";
                if (Owners == "")
                {
                    if (p.group.Permission < this.permissionbuild && (!inZone || !AllowBuild))
                    {
                        p.SendBlockchange(x, y, z, b);
                        Player.SendMessage(p, "Must be at least " + PermissionToName(permissionbuild) + " to build here");
                        return;
                    }
                }*/

                errorLocation = "Block sending";
                /*if (Block.Convert(b) != Block.Convert(type) && !Instant)*/
                //SHOULD BE SEND TO ALL IN RANGE
                //Player.GlobalBlockchange(this, x, y, z, type);

                Position awn = new Position(0, 120, 0);
                if ((Block)b.type == Block.Sponge && physics > 0 && (Block)type != Block.Sponge) PhysSpongeRemoved(new Position(x, y, z));

                
                errorLocation = "Setting tile";
                //p.loginBlocks++;
                //p.overallBlocks++;
                //SetTile(x, y, z, type);      //Updates server level blocks

                //errorLocation = "Growing grass";
                //if (Server.world.GetBlock(x, (ushort)(y - 1), z) == Block.Glass && GrassDestroy && !Block.LightPass(type)) { Blockchange(p, x, (byte)(y - 1), z, (byte)Block.Dirt); }

                errorLocation = "Adding physics";
                if (physics > 0) if (Blocks.PhysicsBlocks.Contains((Block)type))
                    {
                        Position phyCheckPos;
                        phyCheckPos.x = x; 
                        phyCheckPos.y = y; 
                        phyCheckPos.z = z;

                        AddCheck(phyCheckPos);
                    }
            }
            catch (Exception e)
            {
                
            }
        }

        public void PhysBlockchange(int x, byte y, int z, byte type, bool overRide = false, string extraInfo = "")    //Block change made by physics
        {
            //TODO: Get this working with SMP!
            /*if (x < 0 || y < 0 || z < 0) return;
            if (x >= width || y >= depth || z >= height) return;
            byte b = Server.world.GetBlock(x, y, z);

            try
            {
                if (!overRide)
                    if (Block.OPBlocks(b) || Block.OPBlocks(type)) return;

                if (Block.Convert(b) != Block.Convert(type))    //Should save bandwidth sending identical looking blocks, like air/op_air changes.
                    Player.GlobalBlockchange(this, x, y, z, type);

                if (b == Block.sponge && physics > 0 && type != Block.sponge)
                    PhysSpongeRemoved(PosToInt(x, y, z));

                SetTile(x, y, z, type);               //Updates server level blocks

                if (physics > 0)
                    if (Block.Physics(type) || extraInfo != "") AddCheck(PosToInt(x, y, z), extraInfo);
            }
            catch
            {
                SetTile(x, y, z, type);
            }*/
        }

        public void skipChange(int x, byte y, int z, byte type)
        {
            //TODO: Get this working with SMP!
            //if (x < 0 || y < 0 || z < 0) return;
            //if (x >= width || y >= depth || z >= height) return;

            //SetTile(x, y, z, type);
        }

        
        public void setPhysics(int newValue)
        {
            //TODO: Get this working with SMP!
            if (physics == 0 && newValue != 0)
            {
                /*for (int i = 0; i < blocks.Length; i++)
                    // Optimization hack, since no blocks under 183 ever need a restart
                    if (blocks[i] > 183)
                        if (Block.NeedRestart(blocks[i]))
                            AddCheck(i);*/
            }
            physics = newValue;

        }

        public void Physics()
        {
            int wait = speedPhysics;
            while (true)
            {
                try
                {
                retry: if (wait > 0) Thread.Sleep(wait);
                    if (physics == 0 || ListCheck.Count == 0) goto retry;

                    DateTime Start = DateTime.Now;

                    if (physics > 0) CalcPhysics();

                    TimeSpan Took = DateTime.Now - Start;
                    wait = (int)speedPhysics - (int)Took.TotalMilliseconds;

                    if (wait < (int)(-overload * 0.75f))
                    {
                        if (wait < -overload)
                        {
                            ClearPhysics();
                            Logger.Log("Physics shutdown on world!",LogType.Warning);
                            wait = speedPhysics;
                        }
                        else
                        {
                            Logger.Log("Physics warning on world!",LogType.Warning);
                        }
                    }
                }
                catch
                {
                    wait = speedPhysics;
                }
            }
        }

        #region ==Physics==
        public struct Pos { public int x, z; }

        public string foundInfo(int x, byte y, int z)
        {
            Check foundCheck = null;
            /*try
            {
                foundCheck = ListCheck.Find(Check => Check.b == PosToInt(x, y, z));
            }
            catch { }
            if (foundCheck != null)
                return foundCheck.extraInfo;*/
            return "";
        }

        public void CalcPhysics()
        {
            try
            {
                if (physics > 0)
                {
                    int x, z, mx, my, mz;
                    byte y;

                    Random rand = new Random();
                    lastCheck = ListCheck.Count;
                    ListCheck.ForEach(delegate(Check C)
                    {
                        try
                        {
                            bool InnerChange = false; bool skip = false;
                            int storedRand = 0;
                            Player foundPlayer = null; int foundNum = 75, currentNum, newNum, oldNum;
                            string foundInfo = C.extraInfo;

                        newPhysic: if (foundInfo != "")
                            {
                                int currentLoop = 0;
                                if (!foundInfo.Contains("wait")) if ((Block)blocks[C.b] == Block.Air) C.extraInfo = "";

                                bool drop = false; int dropnum = 0;
                                bool wait = false; int waitnum = 0;
                                bool dissipate = false; int dissipatenum = 0;
                                bool revert = false; byte reverttype = 0;
                                bool explode = false; int explodenum = 0;
                                bool finiteWater = false;
                                bool rainbow = false; int rainbownum = 0;
                                bool door = false;

                            startCheck:
                                if (wait)
                                {
                                    int storedInt = 0;
                                    if (waitnum <= C.time)
                                    {
                                        wait = false;
                                        C.extraInfo = C.extraInfo.Substring(0, C.extraInfo.IndexOf("wait ")) + C.extraInfo.Substring(C.extraInfo.IndexOf(' ', C.extraInfo.IndexOf("wait ") + 5) + 1);
                                        goto startCheck;
                                    }
                                    else
                                    {
                                        C.time++;
                                        foundInfo = "";
                                        goto newPhysic;
                                    }
                                }
                                else
                                {
                                    if (finiteWater)
                                        finiteMovement(C, x, y, z);
                                    else
                                    {
                                        if (drop)
                                            if (rand.Next(1, 100) <= dropnum)
                                                if ((Block)Server.world.GetBlock(x, (byte)(y - 1), z).type == Block.Air || (Block)Server.world.GetBlock(x, (byte)(y - 1), z).type == Block.Lava || (Block)Server.world.GetBlock(x, (byte)(y - 1), z).type == Block.Water)
                                                {
                                                    if (rand.Next(1, 100) < int.Parse(C.extraInfo.Split(' ')[1]))
                                                    {
                                                        AddUpdate(new Position(x, (y - 1), z), blocks[C.b], false, C.extraInfo);
                                                        AddUpdate(new Position(x, (y - 1), z), (byte)Block.Air); C.extraInfo = "";
                                                    }
                                                }
                                    }
                                }
                            }
                            else
                            {
                                switch ((Block) blocks[C.b])
                                {
                                    case Block.Dirt:
                                       
                                        break;

                                    case Block.Water:
                                        
                                        break;

                                    default:
                                        if (!C.extraInfo.Contains("wait")) C.time = 255;
                                        break;
                                }
                            }
                        }
                        catch
                        {
                            ListCheck.Remove(C);
                            //Server.s.Log(e.Message);
                        }

                    });

                    ListCheck.RemoveAll(Check => Check.time == 255);  //Remove all that are finished with 255 time

                    lastUpdate = ListUpdate.Count;
                    ListUpdate.ForEach(delegate(Update C)
                    {
                        try
                        {
                            PhysBlockchange((int)C.ps.x, (byte)C.ps.y, (int)C.ps.z, C.type, false, C.extraInfo);
                        }
                        catch
                        {
                            Logger.Log("Phys update issue", LogType.Debug);
                        }
                    });

                    ListUpdate.Clear();
                }
            }
            catch
            {
                Logger.Log("Level physics error", LogType.Debug);
            }
        }
        public void AddCheck(Position p, string extraInfo = "", bool overRide = false)
        {
            try
            {
                if (!ListCheck.Exists(Check => Check.ps == p))
                {
                    ListCheck.Add(new Check(p, extraInfo));    //Adds block to list to be updated
                }
                else
                {
                    if (overRide)
                    {
                        foreach (Check C2 in ListCheck)
                        {
                            if (C2.b == b)
                            {
                                C2.extraInfo = extraInfo;
                                return;
                            }
                        }
                    }
                }
            }
            catch
            {
                //s.Log("Warning-PhysicsCheck");
                //ListCheck.Add(new Check(b));    //Lousy back up plan
            }
        }
        private bool AddUpdate(Position ps, int type, bool overRide = false, string extraInfo = "")
        {
            try
            {
                if (overRide == true)
                {
                    AddCheck(ps, extraInfo);
                    PhysBlockchange((int)ps.x, (byte)ps.y, (int)ps.z, (byte)type, true);
                    return true;
                }

                if (!ListUpdate.Exists(Update => Update.b == b))
                {
                    ListUpdate.Add(new Update(ps, (byte)type, extraInfo));
                    return true;
                }
                else
                {
                    if (type == 12 || type == 13)
                    {
                        //TODO: Remove Duplicates
                        //ListUpdate.RemoveAll(Update => Update.b == b);
                        ListUpdate.Add(new Update(ps, (byte)type, extraInfo));
                        return true;
                    }
                }

                return false;
            }
            catch
            {
                //s.Log("Warning-PhysicsUpdate");
                //ListUpdate.Add(new Update(b, (byte)type));    //Lousy back up plan
                return false;
            }
        }

        public void ClearPhysics()
        {
            ushort x, y, z;
            ListCheck.ForEach(delegate(Check C)
            {
                //TODO Translate this to SMP
                /*IntToPos(C.b, out x, out y, out z);
                //attemps on shutdown to change blocks back into normal selves that are active, hopefully without needing to send into to clients.
                switch (blocks[C.b])
                {
                    case 200:
                    case 202:
                    case 203:
                        blocks[C.b] = 0;
                        break;
                    case 201:
                        //blocks[C.b] = 111;
                        Blockchange(x, y, z, 111);
                        break;
                    case 205:
                        //blocks[C.b] = 113;
                        Blockchange(x, y, z, 113);
                        break;
                    case 206:
                        //blocks[C.b] = 114;
                        Blockchange(x, y, z, 114);
                        break;
                    case 207:
                        //blocks[C.b] = 115;
                        Blockchange(x, y, z, 115);
                        break;
                }

                try
                {
                    if (C.extraInfo.Contains("revert"))
                    {
                        int i = 0;
                        foreach (string s in C.extraInfo.Split(' '))
                        {
                            if (s == "revert")
                            {
                                Blockchange(x, y, z, Byte.Parse(C.extraInfo.Split(' ')[i + 1]));
                                break;
                            }
                            i++;
                        }
                    }
                }
                catch (Exception e)
                {
                    Server.ErrorLog(e);
                }*/
            });

            ListCheck.Clear();
            ListUpdate.Clear();
        }
        //================================================================================================================
        private void PhysWater(Position ps, byte type)
        {
            if (b == -1) { return; }
            switch (blocks[b])
            {
                case 0:
                    if (!PhysSpongeCheck(b))
                    {
                        AddUpdate(b, type);
                    }
                    break;

                case 10:    //hit active_lava
                case 112:    //hit lava_fast
                    if (!PhysSpongeCheck(b)) { AddUpdate(b, 1); }
                    break;

                case 6:
                case 37:
                case 38:
                case 39:
                case 40:
                    if (physics > 1)   //Adv physics kills flowers and mushrooms in water
                    {
                        if (!PhysSpongeCheck(b)) { AddUpdate(b, 0); }
                    }
                    break;

                case 12:    //sand
                case 13:    //gravel
                case 110:   //woodfloat
                    AddCheck(b);
                    break;

                default:
                    break;
            }
        }
        //================================================================================================================
        private void PhysLava(Position ps, byte type)
        {
            if (b == -1) { return; }
            switch (blocks[b])
            {
                case 0:
                    AddUpdate(ps, type);
                    break;

                case 8:    //hit active_water
                    AddUpdate(ps, 1);
                    break;

                case 12:    //sand
                    if (physics > 1)   //Adv physics changes sand to glass next to lava
                    {
                        AddUpdate(ps, 20);
                    }
                    else
                    {
                        AddCheck(ps);
                    }
                    break;

                case 13:    //gravel
                    AddCheck(ps);
                    break;

                case 5:
                case 6:
                case 17:
                case 18:
                case 37:
                case 38:
                case 39:
                case 40:
                    if (physics > 1)   //Adv physics kills flowers and mushrooms plus wood in lava
                    {
                        AddUpdate(b, 0);
                    }
                    break;

                default:
                    break;
            }
        }
        //================================================================================================================
        private bool PhysSand(Position ps, byte type)   //also does gravel
        {
            if (b == -1 || physics == 0) return false;

            Position tempb = ps;
            bool blocked = false;
            bool moved = false;

            do
            {
                tempb.y = tempb.y - 1;     //Get block below each loop
                if ((Block)Server.world.GetBlock((int)tempb.x, (byte)tempb.y, (int)tempb.z).type != Block.Zero)
                {
                    switch (blocks[tempb])
                    {
                        case 0:         //air lava water
                        case 8:
                        case 10:
                            moved = true;
                            break;

                        case 6:
                        case 37:
                        case 38:
                        case 39:
                        case 40:
                            if (physics > 1)   //Adv physics crushes plants with sand
                            { moved = true; }
                            else
                            { blocked = true; }
                            break;

                        default:
                            blocked = true;
                            break;
                    }
                    if (physics > 1) { blocked = true; }
                }
                else
                { blocked = true; }
            }
            while (!blocked);

            if (moved)
            {
                AddUpdate(ps, 0);
                if (physics > 1)
                { AddUpdate(tempb, type); }
                else
                { 
                    ps.y = ps.y - 1;
                    AddUpdate(ps, type); 
                }
            }

            return moved;
        }

        private void PhysSandCheck(Position b)   //also does gravel
        {
            if (b == -1) { return; }
            switch (blocks[b])
            {
                case 12:    //sand
                case 13:    //gravel
                case 110:   //wood_float
                    AddCheck(ps);
                    break;

                default:
                    break;
            }
        }
        //================================================================================================================
        private void PhysStair(Position ps)
        {
            Position tempb = ps;
            tempb.y = ps.y - 1;     //Get block below
            if ((Block)Server.world.GetBlock((int)tempb.x, (byte)tempb.y, (int)tempb.z).type != Block.Zero)
            {
                if (Server.world.GetBlock(tempb) == Block.staircasestep)
                {
                    AddUpdate(b, 0);
                    AddUpdate(tempb, 43);
                }
            }
        }
        //================================================================================================================
        private bool PhysSpongeCheck(Position ps)         //return true if sponge is near
        {
            int temp = 0;
            for (int x = -2; x <= +2; ++x)
            {
                for (int y = -2; y <= +2; ++y)
                {
                    for (int z = -2; z <= +2; ++z)
                    {
                        temp = IntOffset(b, x, y, z);
                        if (Server.world.GetBlock(temp) != Block.Zero)
                        {
                            if (Server.world.GetBlock(temp) == 19) { return true; }
                        }
                    }
                }
            }
            return false;
        }
        //================================================================================================================
        private void PhysSponge(Position ps)         //turn near water into air when placed
        {
            int temp = 0;
            for (int x = -2; x <= +2; ++x)
            {
                for (int y = -2; y <= +2; ++y)
                {
                    for (int z = -2; z <= +2; ++z)
                    {
                        temp = IntOffset(b, x, y, z);
                        if (Server.world.GetBlock(temp) != Block.Zero)
                        {
                            if (Server.world.GetBlock(temp) == 8) { AddUpdate(ps, 0); }
                        }
                    }
                }
            }

        }
        //================================================================================================================
        public void PhysSpongeRemoved(Position ps)         //Reactivates near water
        {
            //TODO Calc only edge
            int temp = 0;
            for (int x = -3; x <= +3; ++x)
            {
                for (int y = -3; y <= +3; ++y)
                {
                    for (int z = -3; z <= +3; ++z)
                    {
                        temp = IntOffset(b, x, y, z);
                        if (Server.world.GetBlock(temp) != Block.Zero)
                        {
                            if (Server.world.GetBlock(temp) == 8) { AddCheck(ps, temp); }
                        }
                    }
                }
            }

        }
        
        //================================================================================================================
        private void PhysFall(byte newBlock, int x, byte y, int z, bool random)
        {
            Random randNum = new Random(); byte b;
            if (random == false)
            {
                b = Server.world.GetBlock((ushort)(x + 1), y, z);
                if (b == Block.Air || b == Block.waterstill) Blockchange((ushort)(x + 1), y, z, newBlock);
                b = Server.world.GetBlock((ushort)(x - 1), y, z);
                if (b == Block.Air || b == Block.waterstill) Blockchange((ushort)(x - 1), y, z, newBlock);
                b = Server.world.GetBlock(x, y, (ushort)(z + 1));
                if (b == Block.Air || b == Block.waterstill) Blockchange(x, y, (ushort)(z + 1), newBlock);
                b = Server.world.GetBlock(x, y, (ushort)(z - 1));
                if (b == Block.Air || b == Block.waterstill) Blockchange(x, y, (ushort)(z - 1), newBlock);
            }
            else
            {
                if (Server.world.GetBlock((ushort)(x + 1), y, z) == Block.Air && randNum.Next(1, 10) < 3) Blockchange((ushort)(x + 1), y, z, newBlock);
                if (Server.world.GetBlock((ushort)(x - 1), y, z) == Block.Air && randNum.Next(1, 10) < 3) Blockchange((ushort)(x - 1), y, z, newBlock);
                if (Server.world.GetBlock(x, y, (ushort)(z + 1)) == Block.Air && randNum.Next(1, 10) < 3) Blockchange(x, y, (ushort)(z + 1), newBlock);
                if (Server.world.GetBlock(x, y, (ushort)(z - 1)) == Block.Air && randNum.Next(1, 10) < 3) Blockchange(x, y, (ushort)(z - 1), newBlock);
            }
        }
        //================================================================================================================
        private void PhysReplace(int b, byte typeA, byte typeB)     //replace any typeA with typeB
        {
            if (b == -1) { return; }
            if (blocks[b] == typeA)
            {
                AddUpdate(b, typeB);
            }
        }
        //================================================================================================================

        public void MakeExplosion(ushort x, ushort y, ushort z, int size)
        {
            int xx, yy, zz; Random rand = new Random(); byte b;

            Position constructPos;
            constructPos.x = xx; constructPos.y = yy; constructPos.z = zz;

            if (physics < 2) return;
            AddUpdate(constructPos, Block.tntexplosion, true);

            for (xx = (x - (size + 1)); xx <= (x + (size + 1)); ++xx)
                for (yy = (y - (size + 1)); yy <= (y + (size + 1)); ++yy)
                    for (zz = (z - (size + 1)); zz <= (z + (size + 1)); ++zz)
                        try
                        {
                            constructPos.x = xx; constructPos.y = yy; constructPos.z = zz;

                            b = Server.world.GetBlock((ushort)xx, (ushort)yy, (ushort)zz);
                            if ((Block) b == Block.tnt)
                            {
                                AddUpdate(constructPos, Block.smalltnt);
                            }
                            else
                            {
                                AddCheck(constructPos);
                            }
                        }
                        catch { }

            for (xx = (x - (size + 2)); xx <= (x + (size + 2)); ++xx)
                for (yy = (y - (size + 2)); yy <= (y + (size + 2)); ++yy)
                    for (zz = (z - (size + 2)); zz <= (z + (size + 2)); ++zz)
                    {
                        constructPos.x = xx; constructPos.y = yy; constructPos.z = zz;

                        ChunkBlock cb = Server.world.GetBlock((int)xx, (byte)yy, (int)zz);
                        if (rand.Next(1, 10) < 7)
                            if ((Block) cb.type != Block.TNT)
                            {
                                
                                if (rand.Next(1, 11) <= 4) AddUpdate(constructPos, Block.tntexplosion);
                                else if (rand.Next(1, 11) <= 8) AddUpdate(constructPos, Block.Air);
                                else AddCheck(constructPos, "drop 50 dissipate 8");
                            }
                        if ((Block)cb.type == Block.TNT)
                        {
                            AddUpdate(constructPos, Block.TNT);
                        }
                    }

            for (xx = (x - (size + 3)); xx <= (x + (size + 3)); ++xx)
                for (yy = (y - (size + 3)); yy <= (y + (size + 3)); ++yy)
                    for (zz = (z - (size + 3)); zz <= (z + (size + 3)); ++zz)
                    {
                        constructPos.x = xx; constructPos.y = yy; constructPos.z = zz;
                        ChunkBlock cb = Server.world.GetBlock((int)xx, (byte)yy, (int)zz);
                        if (rand.Next(1, 10) < 3)
                            if ((Block)cb.type != Block.TNT)
                            {
                                if (rand.Next(1, 11) <= 4) AddUpdate(constructPos, Block.tntexplosion);
                                else if (rand.Next(1, 11) <= 8) AddUpdate(constructPos, Block.Air);
                                else AddCheck(constructPos, "drop 50 dissipate 8");
                            }
                        if ((Block)cb.type == Block.TNT)
                        {
                            AddUpdate(constructPos, Block.TNT);
                        }
                    }
        }

        public void finiteMovement(Check C, int x, byte y, int z)
        {
            Random rand = new Random();
            Position finitePos;
            finitePos.x = x; finitePos.y = y; finitePos.z = z;

            List<int> bufferfiniteWater = new List<int>();
            List<Pos> bufferfiniteWaterList = new List<Pos>();

            if (Server.world.GetBlock(x, (ushort)(y - 1), z) == Block.Air)
            {
                AddUpdate(finitePos, blocks[C.b], false, C.extraInfo);
                AddUpdate(C.b, Block.Air); C.extraInfo = "";
            }
            else if (Server.world.GetBlock(x, (ushort)(y - 1), z) == Block.StillWater || Server.world.GetBlock(x, (ushort)(y - 1), z) == Block.StillLava)
            {
                AddUpdate(C.b, Block.Air); C.extraInfo = "";
            }
            else
            {
                for (int i = 0; i < 25; ++i) bufferfiniteWater.Add(i);

                for (int k = bufferfiniteWater.Count - 1; k > 1; --k)
                {
                    int randIndx = rand.Next(k); //
                    int temp = bufferfiniteWater[k];
                    bufferfiniteWater[k] = bufferfiniteWater[randIndx]; // move random num to end of list.
                    bufferfiniteWater[randIndx] = temp;
                }

                Pos pos;

                for (ushort xx = (ushort)(x - 2); xx <= x + 2; ++xx)
                {
                    for (ushort zz = (ushort)(z - 2); zz <= z + 2; ++zz)
                    {
                        pos.x = xx; pos.z = zz;
                        bufferfiniteWaterList.Add(pos);
                    }
                }

                foreach (int i in bufferfiniteWater)
                {
                    pos = bufferfiniteWaterList[i];
                    if (Server.world.GetBlock(pos.x, (ushort)(y - 1), pos.z) == Block.Air && Server.world.GetBlock(pos.x, y, pos.z) == Block.Air)
                    {
                        if (pos.x < x) pos.x = (ushort)(Math.Floor((double)(pos.x + x) / 2)); else pos.x = (ushort)(Math.Ceiling((double)(pos.x + x) / 2));
                        if (pos.z < z) pos.z = (ushort)(Math.Floor((double)(pos.z + z) / 2)); else pos.z = (ushort)(Math.Ceiling((double)(pos.z + z) / 2));

                        if (Server.world.GetBlock(pos.x, y, pos.z) == Block.Air)
                        {
                            if (AddUpdate(PosToInt(pos.x, y, pos.z), blocks[C.b], false, C.extraInfo))
                            {
                                AddUpdate(C.b, Block.Air); C.extraInfo = "";
                                break;
                            }
                        }
                    }
                }
            } 
        }
        #endregion
    }
}

